﻿#include "jsonpath.h"
#include <QVariant>
#include <QJsonArray>
#include <QJsonObject>
#include <QtWidgets>


#define STR_SPRINTF(path) \
    va_list ap; \
    va_start(ap, cformat); \
    path = QString::vasprintf(cformat, ap); \


JsonPathReader::JsonPathReader(QObject *parent)
    : QObject(parent)
{

}
JsonPathReader::JsonPathReader(const QByteArray &byte, QObject *parent)
    : QObject(parent)
{
    parse(byte);
}

void JsonPathReader::parse(const QByteArray &byte)
{
    QJsonDocument doc = QJsonDocument::fromJson(byte, &err);
    if(doc.isArray()) {
        json = doc.array();
    } else if(doc.isObject()) {
        json = doc.object();
    }
}

QJsonValue JsonPathReader::jsonValue(const char *cformat, ...)
{
    QString path;STR_SPRINTF(path);
    return _jsonValue(path);
}

QJsonValue JsonPathReader::JsonValue(const QJsonValue &var, const char *cformat, ...)
{
    QString path;STR_SPRINTF(path);
    return _jsonValue(var, path);
}

QVariant JsonPathReader::value(const char *cformat, ...)
{
    QString path;STR_SPRINTF(path);
    QJsonValue var = _jsonValue(path);
    //
    if(var.isArray() || var.isObject()) {
        return toJson(var);
    }
    return var.toVariant();
}

QVariant JsonPathReader::valueList(const char *cformat, ...)
{
    QString path;STR_SPRINTF(path);
    QJsonValue var = _jsonValue(path);
    return var.toVariant();
}

QVariant JsonPathReader::Value(QJsonValue var, const char *cformat, ...)
{
    QString path;STR_SPRINTF(path);
    var = _jsonValue(var, path);

    if(var.isArray() || var.isObject()) {
        return toJson(var);
    }
    return var.toVariant();
}

int JsonPathReader::arrayCount(const char *cformat, ...)
{
    QString path;STR_SPRINTF(path);
    return _arrayCount(path);
}

QJsonParseError::ParseError JsonPathReader::errorType()
{
    return err.error;
}

QString JsonPathReader::errString()
{
    return err.errorString();
}

QStringList JsonPathReader::path2List(const QString &path)
{
    QStringList list;
    for(int k = 0; k < path.count(); k ++) {
        if(path.mid(k, 1) == "[") {
            list << "[";
        } else if(path.mid(k, 1) == "/"){
            list << "{";
        } else if(k == 0) {
            list << "{" + path.mid(0, 1);
        } else {
            list.last() += path.mid(k, 1);
        }
    }
    return list;
}

QJsonValue JsonPathReader::_jsonValue(const QString &path)
{
    if(err.error != QJsonParseError::NoError) {
        return QJsonValue();
    }
    return _jsonValue(json, path);
}

QJsonValue JsonPathReader::_jsonValue(QJsonValue var, const QString &path)
{
    QStringList list = path2List(path);
    //
    for(int k = 0; k < list.count(); k ++) {
        QString type = list[k].mid(0, 1);
        QString name = list[k].mid(1);
        if(type == "[") {
            var = var.toArray().at(name.toInt());
        } else if(type == "{") {
            var = var.toObject().value(name);
        }
    }
    return var;
}

int JsonPathReader::_arrayCount(const QString &path)
{
    QJsonValue var = _jsonValue(path);
    if(var.isArray()) {
        return var.toArray().count();
    }
    return 0;
}

QByteArray JsonPathReader::toJson(const QJsonValue &var)
{
    QJsonDocument doc;
    if(var.isObject()) {
        doc.setObject(var.toObject());
    } else if(var.isArray()) {
        doc.setArray(var.toArray());
    }
    return doc.toJson();
}

JsonPathWriter::JsonPathWriter(QObject *parent)
{
}

void JsonPathWriter::parse(const QByteArray &byte)
{
    QJsonDocument doc = QJsonDocument::fromJson(byte);
    json = doc.toVariant();
}


bool JsonPathWriter::setValue(const QString &path, const QVariant &var)
{
    QStringList list = JsonPathReader::path2List(path);
    QVariant *v = mapValuePtr(path);
    if(v == nullptr)
        return false;
    *v = var;
    return true;
}

bool JsonPathWriter::append(const QString &path, const QMap<QString, QVariant> &obj, bool doClear)
{
    QStringList list = JsonPathReader::path2List(path);
    QVariant *v = mapValuePtr(path);
    if(v == nullptr)
        return false;
    if(QString("QVariantList") != v->typeName())
        return false;
    QVariantList *arr= (QVariantList *)v;
    if(doClear) {
        arr->clear();
    }
    arr->append(obj);
    return true;
}

QByteArray JsonPathWriter::toJson()
{
    QJsonDocument doc;
    QJsonValue var = QJsonValue::fromVariant(json);
    if(var.isObject()) {
        doc.setObject(var.toObject());
    } else if(var.isArray()) {
        doc.setArray(var.toArray());
    }
    return doc.toJson();
}


QVariant *JsonPathWriter::mapValuePtr(const QString &path)
{
    QStringList list = JsonPathReader::path2List(path);
    //
    QMap<QString, QVariant> *obj;
    QVariantList *array;
    QVariant *var = &json;
    for(int k = 0; k < list.count(); k ++) {
        if(var == nullptr)
            break;

        QString type = list[k].mid(0, 1);
        QString name = list[k].mid(1);
        if(type == "[") {
            array = (QVariantList *)var;
            if(0 <= name.toInt() && name.toInt() < array->count())
                var = &(*array)[name.toInt()];
            else
                var = nullptr;
        } else if(type == "{") {
            obj = (QMap<QString, QVariant> *)var;
            if(obj->contains(name))
                var = &(*obj)[name];
            else
                var = nullptr;
        }
    }
    return var;
}
